home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-9.10-netbook-remix-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / dbus / bus.py < prev    next >
Encoding:
Python Source  |  2009-05-06  |  17.6 KB  |  440 lines

  1. # Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/>
  2. #
  3. # Permission is hereby granted, free of charge, to any person
  4. # obtaining a copy of this software and associated documentation
  5. # files (the "Software"), to deal in the Software without
  6. # restriction, including without limitation the rights to use, copy,
  7. # modify, merge, publish, distribute, sublicense, and/or sell copies
  8. # of the Software, and to permit persons to whom the Software is
  9. # furnished to do so, subject to the following conditions:
  10. #
  11. # The above copyright notice and this permission notice shall be
  12. # included in all copies or substantial portions of the Software.
  13. #
  14. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15. # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  18. # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  19. # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  21. # DEALINGS IN THE SOFTWARE.
  22.  
  23. __all__ = ('BusConnection',)
  24. __docformat__ = 'reStructuredText'
  25.  
  26. import logging
  27. import weakref
  28.  
  29. from _dbus_bindings import validate_interface_name, validate_member_name,\
  30.                            validate_bus_name, validate_object_path,\
  31.                            validate_error_name,\
  32.                            BUS_SESSION, BUS_STARTER, BUS_SYSTEM, \
  33.                            DBUS_START_REPLY_SUCCESS, \
  34.                            DBUS_START_REPLY_ALREADY_RUNNING, \
  35.                            BUS_DAEMON_NAME, BUS_DAEMON_PATH, BUS_DAEMON_IFACE,\
  36.                            NAME_FLAG_ALLOW_REPLACEMENT, \
  37.                            NAME_FLAG_DO_NOT_QUEUE, \
  38.                            NAME_FLAG_REPLACE_EXISTING, \
  39.                            RELEASE_NAME_REPLY_NON_EXISTENT, \
  40.                            RELEASE_NAME_REPLY_NOT_OWNER, \
  41.                            RELEASE_NAME_REPLY_RELEASED, \
  42.                            REQUEST_NAME_REPLY_ALREADY_OWNER, \
  43.                            REQUEST_NAME_REPLY_EXISTS, \
  44.                            REQUEST_NAME_REPLY_IN_QUEUE, \
  45.                            REQUEST_NAME_REPLY_PRIMARY_OWNER
  46. from dbus.connection import Connection
  47. from dbus.exceptions import DBusException
  48. from dbus.lowlevel import HANDLER_RESULT_NOT_YET_HANDLED
  49.  
  50.  
  51. _NAME_OWNER_CHANGE_MATCH = ("type='signal',sender='%s',"
  52.                             "interface='%s',member='NameOwnerChanged',"
  53.                             "path='%s',arg0='%%s'"
  54.                             % (BUS_DAEMON_NAME, BUS_DAEMON_IFACE,
  55.                                BUS_DAEMON_PATH))
  56. """(_NAME_OWNER_CHANGE_MATCH % sender) matches relevant NameOwnerChange
  57. messages"""
  58.  
  59. _NAME_HAS_NO_OWNER = 'org.freedesktop.DBus.Error.NameHasNoOwner'
  60.  
  61. _logger = logging.getLogger('dbus.bus')
  62.  
  63.  
  64. class NameOwnerWatch(object):
  65.     __slots__ = ('_match', '_pending_call')
  66.  
  67.     def __init__(self, bus_conn, bus_name, callback):
  68.         validate_bus_name(bus_name)
  69.  
  70.         def signal_cb(owned, old_owner, new_owner):
  71.             callback(new_owner)
  72.  
  73.         def error_cb(e):
  74.             if e.get_dbus_name() == _NAME_HAS_NO_OWNER:
  75.                 callback('')
  76.             else:
  77.                 logging.basicConfig()
  78.                 _logger.debug('GetNameOwner(%s) failed:', bus_name,
  79.                               exc_info=(e.__class__, e, None))
  80.  
  81.         self._match = bus_conn.add_signal_receiver(signal_cb,
  82.                                                    'NameOwnerChanged',
  83.                                                    BUS_DAEMON_IFACE,
  84.                                                    BUS_DAEMON_NAME,
  85.                                                    BUS_DAEMON_PATH,
  86.                                                    arg0=bus_name)
  87.         self._pending_call = bus_conn.call_async(BUS_DAEMON_NAME,
  88.                                                  BUS_DAEMON_PATH,
  89.                                                  BUS_DAEMON_IFACE,
  90.                                                  'GetNameOwner',
  91.                                                  's', (bus_name,),
  92.                                                  callback, error_cb,
  93.                                                  utf8_strings=True)
  94.  
  95.     def cancel(self):
  96.         if self._match is not None:
  97.             self._match.remove()
  98.         if self._pending_call is not None:
  99.             self._pending_call.cancel()
  100.         self._match = None
  101.         self._pending_call = None
  102.  
  103.  
  104. class BusConnection(Connection):
  105.     """A connection to a D-Bus daemon that implements the
  106.     ``org.freedesktop.DBus`` pseudo-service.
  107.  
  108.     :Since: 0.81.0
  109.     """
  110.  
  111.     TYPE_SESSION    = BUS_SESSION
  112.     """Represents a session bus (same as the global dbus.BUS_SESSION)"""
  113.  
  114.     TYPE_SYSTEM     = BUS_SYSTEM
  115.     """Represents the system bus (same as the global dbus.BUS_SYSTEM)"""
  116.  
  117.     TYPE_STARTER = BUS_STARTER
  118.     """Represents the bus that started this service by activation (same as
  119.     the global dbus.BUS_STARTER)"""
  120.  
  121.     START_REPLY_SUCCESS = DBUS_START_REPLY_SUCCESS
  122.     START_REPLY_ALREADY_RUNNING = DBUS_START_REPLY_ALREADY_RUNNING
  123.  
  124.     def __new__(cls, address_or_type=TYPE_SESSION, mainloop=None):
  125.         bus = cls._new_for_bus(address_or_type, mainloop=mainloop)
  126.  
  127.         # _bus_names is used by dbus.service.BusName!
  128.         bus._bus_names = weakref.WeakValueDictionary()
  129.  
  130.         bus._signal_sender_matches = {}
  131.         """Map from SignalMatch to NameOwnerWatch."""
  132.  
  133.         return bus
  134.  
  135.     def add_signal_receiver(self, handler_function, signal_name=None,
  136.                             dbus_interface=None, bus_name=None,
  137.                             path=None, **keywords):
  138.         named_service = keywords.pop('named_service', None)
  139.         if named_service is not None:
  140.             if bus_name is not None:
  141.                 raise TypeError('bus_name and named_service cannot both be '
  142.                                 'specified')
  143.             bus_name = named_service
  144.             from warnings import warn
  145.             warn('Passing the named_service parameter to add_signal_receiver '
  146.                  'by name is deprecated: please use positional parameters',
  147.                  DeprecationWarning, stacklevel=2)
  148.  
  149.         match = super(BusConnection, self).add_signal_receiver(
  150.                 handler_function, signal_name, dbus_interface, bus_name,
  151.                 path, **keywords)
  152.  
  153.         if (bus_name is not None and bus_name != BUS_DAEMON_NAME):
  154.             if bus_name[:1] == ':':
  155.                 def callback(new_owner):
  156.                     if new_owner == '':
  157.                         match.remove()
  158.             else:
  159.                 callback = match.set_sender_name_owner
  160.             watch = self.watch_name_owner(bus_name, callback)
  161.             self._signal_sender_matches[match] = watch
  162.  
  163.         self.add_match_string(str(match))
  164.  
  165.         return match
  166.  
  167.     def _clean_up_signal_match(self, match):
  168.         # The signals lock is no longer held here (it was in <= 0.81.0)
  169.         self.remove_match_string_non_blocking(str(match))
  170.         watch = self._signal_sender_matches.pop(match, None)
  171.         if watch is not None:
  172.             watch.cancel()
  173.  
  174.     def activate_name_owner(self, bus_name):
  175.         if (bus_name is not None and bus_name[:1] != ':'
  176.             and bus_name != BUS_DAEMON_NAME):
  177.             try:
  178.                 return self.get_name_owner(bus_name)
  179.             except DBusException, e:
  180.                 if e.get_dbus_name() != _NAME_HAS_NO_OWNER:
  181.                     raise
  182.                 # else it doesn't exist: try to start it
  183.                 self.start_service_by_name(bus_name)
  184.                 return self.get_name_owner(bus_name)
  185.         else:
  186.             # already unique
  187.             return bus_name
  188.  
  189.     def get_object(self, bus_name, object_path, introspect=True,
  190.                    follow_name_owner_changes=False, **kwargs):
  191.         """Return a local proxy for the given remote object.
  192.  
  193.         Method calls on the proxy are translated into method calls on the
  194.         remote object.
  195.  
  196.         :Parameters:
  197.             `bus_name` : str
  198.                 A bus name (either the unique name or a well-known name)
  199.                 of the application owning the object. The keyword argument
  200.                 named_service is a deprecated alias for this.
  201.             `object_path` : str
  202.                 The object path of the desired object
  203.             `introspect` : bool
  204.                 If true (default), attempt to introspect the remote
  205.                 object to find out supported methods and their signatures
  206.             `follow_name_owner_changes` : bool
  207.                 If the object path is a well-known name and this parameter
  208.                 is false (default), resolve the well-known name to the unique
  209.                 name of its current owner and bind to that instead; if the
  210.                 ownership of the well-known name changes in future,
  211.                 keep communicating with the original owner.
  212.                 This is necessary if the D-Bus API used is stateful.
  213.  
  214.                 If the object path is a well-known name and this parameter
  215.                 is true, whenever the well-known name changes ownership in
  216.                 future, bind to the new owner, if any.
  217.  
  218.                 If the given object path is a unique name, this parameter
  219.                 has no effect.
  220.  
  221.         :Returns: a `dbus.proxies.ProxyObject`
  222.         :Raises `DBusException`: if resolving the well-known name to a
  223.             unique name fails
  224.         """
  225.         if follow_name_owner_changes:
  226.             self._require_main_loop()   # we don't get the signals otherwise
  227.  
  228.         named_service = kwargs.pop('named_service', None)
  229.         if named_service is not None:
  230.             if bus_name is not None:
  231.                 raise TypeError('bus_name and named_service cannot both '
  232.                                 'be specified')
  233.             from warnings import warn
  234.             warn('Passing the named_service parameter to get_object by name '
  235.                  'is deprecated: please use positional parameters',
  236.                  DeprecationWarning, stacklevel=2)
  237.             bus_name = named_service
  238.         if kwargs:
  239.             raise TypeError('get_object does not take these keyword '
  240.                             'arguments: %s' % ', '.join(kwargs.iterkeys()))
  241.  
  242.         return self.ProxyObjectClass(self, bus_name, object_path,
  243.                                      introspect=introspect,
  244.                                      follow_name_owner_changes=follow_name_owner_changes)
  245.  
  246.     def get_unix_user(self, bus_name):
  247.         """Get the numeric uid of the process owning the given bus name.
  248.  
  249.         :Parameters:
  250.             `bus_name` : str
  251.                 A bus name, either unique or well-known
  252.         :Returns: a `dbus.UInt32`
  253.         :Since: 0.80.0
  254.         """
  255.         validate_bus_name(bus_name)
  256.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  257.                                   BUS_DAEMON_IFACE, 'GetConnectionUnixUser',
  258.                                   's', (bus_name,))
  259.  
  260.     def start_service_by_name(self, bus_name, flags=0):
  261.         """Start a service which will implement the given bus name on this Bus.
  262.  
  263.         :Parameters:
  264.             `bus_name` : str
  265.                 The well-known bus name to be activated.
  266.             `flags` : dbus.UInt32
  267.                 Flags to pass to StartServiceByName (currently none are
  268.                 defined)
  269.  
  270.         :Returns: A tuple of 2 elements. The first is always True, the
  271.             second is either START_REPLY_SUCCESS or
  272.             START_REPLY_ALREADY_RUNNING.
  273.  
  274.         :Raises `DBusException`: if the service could not be started.
  275.         :Since: 0.80.0
  276.         """
  277.         validate_bus_name(bus_name)
  278.         return (True, self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  279.                                          BUS_DAEMON_IFACE,
  280.                                          'StartServiceByName',
  281.                                          'su', (bus_name, flags)))
  282.  
  283.     # XXX: it might be nice to signal IN_QUEUE, EXISTS by exception,
  284.     # but this would not be backwards-compatible
  285.     def request_name(self, name, flags=0):
  286.         """Request a bus name.
  287.  
  288.         :Parameters:
  289.             `name` : str
  290.                 The well-known name to be requested
  291.             `flags` : dbus.UInt32
  292.                 A bitwise-OR of 0 or more of the flags
  293.                 `NAME_FLAG_ALLOW_REPLACEMENT`,
  294.                 `NAME_FLAG_REPLACE_EXISTING`
  295.                 and `NAME_FLAG_DO_NOT_QUEUE`
  296.         :Returns: `REQUEST_NAME_REPLY_PRIMARY_OWNER`,
  297.             `REQUEST_NAME_REPLY_IN_QUEUE`,
  298.             `REQUEST_NAME_REPLY_EXISTS` or
  299.             `REQUEST_NAME_REPLY_ALREADY_OWNER`
  300.         :Raises `DBusException`: if the bus daemon cannot be contacted or
  301.             returns an error.
  302.         """
  303.         validate_bus_name(name, allow_unique=False)
  304.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  305.                                   BUS_DAEMON_IFACE, 'RequestName',
  306.                                   'su', (name, flags))
  307.  
  308.     def release_name(self, name):
  309.         """Release a bus name.
  310.  
  311.         :Parameters:
  312.             `name` : str
  313.                 The well-known name to be released
  314.         :Returns: `RELEASE_NAME_REPLY_RELEASED`,
  315.             `RELEASE_NAME_REPLY_NON_EXISTENT`
  316.             or `RELEASE_NAME_REPLY_NOT_OWNER`
  317.         :Raises `DBusException`: if the bus daemon cannot be contacted or
  318.             returns an error.
  319.         """
  320.         validate_bus_name(name, allow_unique=False)
  321.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  322.                                   BUS_DAEMON_IFACE, 'ReleaseName',
  323.                                   's', (name,))
  324.  
  325.     def list_names(self):
  326.         """Return a list of all currently-owned names on the bus.
  327.  
  328.         :Returns: a dbus.Array of dbus.UTF8String
  329.         :Since: 0.81.0
  330.         """
  331.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  332.                                   BUS_DAEMON_IFACE, 'ListNames',
  333.                                   '', (), utf8_strings=True)
  334.  
  335.     def list_activatable_names(self):
  336.         """Return a list of all names that can be activated on the bus.
  337.  
  338.         :Returns: a dbus.Array of dbus.UTF8String
  339.         :Since: 0.81.0
  340.         """
  341.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  342.                                   BUS_DAEMON_IFACE, 'ListNames',
  343.                                   '', (), utf8_strings=True)
  344.  
  345.     def get_name_owner(self, bus_name):
  346.         """Return the unique connection name of the primary owner of the
  347.         given name.
  348.  
  349.         :Raises `DBusException`: if the `bus_name` has no owner
  350.         :Since: 0.81.0
  351.         """
  352.         validate_bus_name(bus_name, allow_unique=False)
  353.         return self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  354.                                   BUS_DAEMON_IFACE, 'GetNameOwner',
  355.                                   's', (bus_name,), utf8_strings=True)
  356.  
  357.     def watch_name_owner(self, bus_name, callback):
  358.         """Watch the unique connection name of the primary owner of the
  359.         given name.
  360.  
  361.         `callback` will be called with one argument, which is either the
  362.         unique connection name, or the empty string (meaning the name is
  363.         not owned).
  364.  
  365.         :Since: 0.81.0
  366.         """
  367.         return NameOwnerWatch(self, bus_name, callback)
  368.  
  369.     def name_has_owner(self, bus_name):
  370.         """Return True iff the given bus name has an owner on this bus.
  371.  
  372.         :Parameters:
  373.             `bus_name` : str
  374.                 The bus name to look up
  375.         :Returns: a `bool`
  376.         """
  377.         return bool(self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  378.                                        BUS_DAEMON_IFACE, 'NameHasOwner',
  379.                                        's', (bus_name,)))
  380.  
  381.     def add_match_string(self, rule):
  382.         """Arrange for this application to receive messages on the bus that
  383.         match the given rule. This version will block.
  384.  
  385.         :Parameters:
  386.             `rule` : str
  387.                 The match rule
  388.         :Raises `DBusException`: on error.
  389.         :Since: 0.80.0
  390.         """
  391.         self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  392.                            BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,))
  393.  
  394.     # FIXME: add an async success/error handler capability?
  395.     # (and the same for remove_...)
  396.     def add_match_string_non_blocking(self, rule):
  397.         """Arrange for this application to receive messages on the bus that
  398.         match the given rule. This version will not block, but any errors
  399.         will be ignored.
  400.  
  401.  
  402.         :Parameters:
  403.             `rule` : str
  404.                 The match rule
  405.         :Raises `DBusException`: on error.
  406.         :Since: 0.80.0
  407.         """
  408.         self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  409.                         BUS_DAEMON_IFACE, 'AddMatch', 's', (rule,),
  410.                         None, None)
  411.  
  412.     def remove_match_string(self, rule):
  413.         """Arrange for this application to receive messages on the bus that
  414.         match the given rule. This version will block.
  415.  
  416.         :Parameters:
  417.             `rule` : str
  418.                 The match rule
  419.         :Raises `DBusException`: on error.
  420.         :Since: 0.80.0
  421.         """
  422.         self.call_blocking(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  423.                            BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,))
  424.  
  425.     def remove_match_string_non_blocking(self, rule):
  426.         """Arrange for this application to receive messages on the bus that
  427.         match the given rule. This version will not block, but any errors
  428.         will be ignored.
  429.  
  430.  
  431.         :Parameters:
  432.             `rule` : str
  433.                 The match rule
  434.         :Raises `DBusException`: on error.
  435.         :Since: 0.80.0
  436.         """
  437.         self.call_async(BUS_DAEMON_NAME, BUS_DAEMON_PATH,
  438.                         BUS_DAEMON_IFACE, 'RemoveMatch', 's', (rule,),
  439.                         None, None)
  440.